Basic4GL, Copyright (C) 2004 Tom Mulgrew

Programmer's guide

18-Nov-2004
Tom Mulgrew

This document

This document describes the various functions, and how to use them to do something useful in Basic4GL.
It does not go into great detail about the language syntax itself (see the Language Guide if that is what you need).
It does not go into detail about OpenGL programming (see the OpenGL guide for that.)

Text output

Basic text output

Print and Printr

Basic text output is performed using the "Print" or "Printr" function.

Format:

Print text-string

Or:

Printr text-string

These functions output a string to the screen at the current cursor position and advance the cursor.
The "Printr" function will also generate a return/newline.

Unlike the traditional BASIC "Print" command, there is no special syntax for the supplied argument(s). The following produces a compiler error:

Print "A = "; a; ". B = "; b

However, by using Basic4GLs string concatenation and automatic type conversion, the above can be rewritten as:

Printr "A = " + a + ". B = " + b

Example:

Printr "Hello"
Printr "and welcome to"
Printr "Basic4GL"

Locate

Locate positions the text cursor on the screen.

Format:

Locate X-position, Y-position

The Basic4GL text cursor is invisible. It determines to where on the screen "Print" and "Printr" will write.

By default the Basic4GL displays 40 characters across by 25 characters down (this can be changed using the "ResizeText()" function).

The topmost row is row 0.
The leftmost column is column 0.

Example:

Dim d#
While True
Cls
Locate Sin (d#) * 15 + 18, 10
Print "Hello"
Sleep (100)
d# = d# + 0.1
Wend

Color

Sets the text colour.

Format:

Color (red, green, blue)

Where red, green and blue are integers between 0 and 255 inclusive indicating the intensity of their respective colour component.

Once the text colour is set, any text printed will be in that colour until the text colour is changed.

Example:

dim t
TextMode (TEXT_BUFFERED)
while true

for t = 1 to 10: color (rnd()%255, rnd()%255, rnd()%255): print chr$(rnd()%255): next
DrawText ()

wend

Cls

Cls clears all text from the screen and repositions the cursor to the top left.

ClearLine

ClearLine () clears the current line (the one which the cursor is on).

Example:

dim i
SetTextScroll (false)
for i = 0 to 24: printr i: next
locate 0, 10
ClearLine ()		' Line 10 is cleared

ClearRegion

Cears a rectangular region of the screen.

Format:

ClearRegion (x1, y1, x2, y2)

Where x1, y1, x2, y2 are integers that define the top left column and row (x1, y1) and the bottom right column and row (x2, y2) of the rectangular region to be cleared.

Example:

dim x, y
SetTextScroll (false)
TextMode (TEXT_BUFFERED)
for y = 1 to TextRows ()
    for x = 1 to TextCols ()
        print "#"
    next
next
ClearRegion (5, 5, 35, 9)
locate 13, 7: print "Cleared region"
DrawText ()

TextRows, TextCols and ResizeText

TextRows () returns the number of text columns.
TextCols () returns the number of text rows.

ResizeText (x, y) resizes the text display to y rows by x columns and clears the text.

Example:

dim i, a$
a$ = "Basic4GL"
i = 100
while i >= 4
ResizeText (i * 2 + 1, i + 1)
Locate (TextCols() - Len(a$)) / 2, TextRows() / 2
Print a$
Sleep (50)
i = i - 2
wend

Text scrolling

Advancing the cursor past the end of the line causes it to wrap around onto the next line.

Advancing the cursor past the end of the bottom-most line, or performing a Printr on the bottom-most line causes the text to scroll up by one line.

Example 1:

Print glGetString (GL_EXTENSIONS)

Example 2:

dim d#
while true
locate sin(d#)*15+17, TextRows()-1
Printr "Hello"
Sleep (50)
d# = d# + 0.3
wend

Alternatively you can disable text scrolling with the TextScroll command.

SetTextScroll

SetTextScroll () enables or disables text scrolling when the cursor reaches the bottom of the text screen.

Format:

SetTextScroll (scroll)

Where scroll can equal true to enable text scrolling or false to disable it. Text scrolling is enabled by default.

Example:

SetTextScroll (false)
dim row
print "########################################"
for row = 2 to 24
    print "#                                      #"
next
print "########################################"

TextScroll

TextScroll () returns true if text scrolling is enabled, or false if it isn't.

Fonts

Basic4GL fonts are special transparent images, consisting of a 16 x 16 grid of characters. You can set a new font by calling:

Font (texture)

Where texture is an OpenGL texture handle (usually returned from LoadTexture() or LoadMipmapTexture()).

Example:

printr "Normal font"
dim texture
texture = LoadTexture ("data\charset2.png")
Font (texture)
printr "charset2.png font"

To get the texture handle for the default font, call:

DefaultFont ()

Example:

dim texture
texture = LoadTexture ("data\charset2.png")
Font (texture)
printr "charset2.png font"
Font (DefaultFont ())
printr "Normal font"

Text modes

Basic4GL has 3 different modes for rendering text on the screen. You choose one by executing the appropriate TextMode() call:

The default mode is TEXT_SIMPLE.
In this mode, Basic4GL redraws the screen after each "Print", "Printr", "Cls" or "ResizeText()".

This mode is easy to use, and the results are instant. However there are a number of situations where you may find it favourable to use TEXT_BUFFERED.

In TEXT_BUFFERED mode, Basic4GL does not update the screen until you call DrawText ().
This has advantages if you are animating a large amount of text:

  1. Reduces flicker.
    The screen is only updated once all text has been drawn.
  2. Reduces screen resync delay.
    Depending on your video card and OpenGL settings, your OpenGL system may wait for vertical syncronisation before every screen update.
    This can lead to unnecessarily slow animations in TEXT_SIMPLE mode, as Basic4GL must stop and wait for vertical resync after every "Print" statement.

However, you must remember to call DrawText() or the user won't see any changes.

Example:

TextMode (TEXT_BUFFERED)
dim d#, t
while true
for t = 1 to 10
Locate sin(d#*t/19.0+t)*14+14,t*2+1
print " Thing "
next
DrawText ()
Sleep (10)
d# = d# + .1
wend

TEXT_OVERLAID mode is used to combine OpenGL graphics with text.
This mode is necessary if you wish to use OpenGL graphics commands and text at the same time.

This would cause problems in TEXT_SIMPLE or TEXT_BUFFERED mode, as both modes automatically clear the screen before rendering the text.

In TEXT_OVERLAID mode the DrawText() function will not clear the screen, or copy the result to the front buffer. It will simply render the current text transparently over the top of the current scene.
You must therefore manually clear the screen and swap it to the font buffer at the appropriate times.
Example:

TextMode (TEXT_OVERLAID)
locate 12, 12: print "This is a square"

dim a#
while true
glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity ()
glTranslatef (0, 0, -2)
glRotatef (a#, 0, 0, 1)
glBegin (GL_QUADS)
glColor3f (1, 0, 0): glVertex2f ( 1, 1)
glColor3f (0, 1, 0): glVertex2f (-1, 1)
glColor3f (0, 0, 1): glVertex2f (-1,-1)
glColor3f (1, 1, 1): glVertex2f ( 1,-1)
glEnd ()
DrawText ()
SwapBuffers ()
a# = a# + 0.3
wend

Reading from the screen

CharAt$

CharAt$(x, y) returns the character at column x and row y.

Example:

TextMode(TEXT_BUFFERED)
dim d#, t, x, y, crash: crash = false: x = TextCols()/2
while not crash
for t = 1 to 5: locate sin(d#+t)*15+15,t*2+2: print" Thing! ": next
y=y-1
if y<0 then
y = TextRows()-1: cls
else
if ScanKeyDown(VK_LEFT) and x > 2 then x = x - 1 endif
if ScanKeyDown(VK_RIGHT) and x < 36 then x = x + 1 endif
crash = CharAt$(x,y)<>" "
locate x, y: print"X"
endif
DrawText()
WaitTimer (80)
d# = d#+0.06
wend

Timing

Sleep

Pauses execution for a number of milliseconds.

Format:

Sleep (milliseconds)

Note: The application is completely unresponsive while sleeping. Therefore Basic4GL will not sleep for more than 5000 msec (5 seconds) at a time.
To sleep for more than 5 seconds, use a loop.
For example:

Dim i
For i = 1 to 60: Sleep (1000): Next

Will pause for 60 seconds, but still give the user the opportunity to break out of the program if he/she wishes.

WaitTimer, SyncTimer and ResetTimer

WaitTimer

This function is similar to Sleep, and indeed has the same format:

WaitTimer (milliseconds)

The difference is that WaitTimer waits until milliseconds milliseconds has elapsed from the previous WaitTimer call.

This difference is significant if WaitTimer is used inside an animation loop, with other code that may take some time to execute (such as rendering a frame).
For example:

While true
Draw a frame
WaitTimer (100)
Wend

If Draw a frame were to take 40 milliseconds, then WaitTimer will pause for only 60 milliseconds, ensuring that the loop is correctly iterated 10 times a second.

Even simple animations can potentially take up to the resync period of the monitor (anything from 1/100th to 1/50th of a second), if the user's graphics card is configured to wait for retrace before drawing.

SyncTimer

SyncTimer returns true if you need to update the internal state of the application to catch up to the clock.

This can be used to force an animation to update internally so many times per second, regardless of a PC's rendering speed, and is intended to be used as follows:

While main-loop-condition
Render scene
While SyncTimer (delay)
Update state
Wend

For example, if delay was 10 milliseconds, then Update state will execute 100 times per second, regardless of whether the computer is capable of rendering 20 or 100 frames per second.

Example:

dim x, y, a#, b#
while true
glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity ()
glTranslatef (0, 0, -16)
glRotatef (a#, 0, 0, 1)
for y = -5 to 5: for x = -5 to 5
glPushMatrix ()
glTranslatef (x * 3, y * 3, 0)
glRotatef ((x + y) * 60 + b#, 1, 0, 0)
glBegin (GL_QUADS)
glColor3f (1, 0, 0): glVertex2f ( 1, 1)
glColor3f (0, 1, 0): glVertex2f (-1, 1)
glColor3f (0, 0, 1): glVertex2f (-1,-1)
glColor3f (1, 1, 1): glVertex2f ( 1,-1)
glEnd ()
glPopMatrix ()
Next: Next
SwapBuffers ()
while SyncTimer (10)
a# = a# + 0.9: b# = b# + 3.6
wend
wend

Keyboard input

Text input

Input$

Reads a text string from the keyboard.

Format:

Input$ ()

Input$ () will pause the program and wait until the user types in some text and hits enter. The text will be displayed on the screen as the user types.
The program will then continue, and Input$() will return the text that was entered.

Example:

dim name$
print "Please enter your name: "
name$ = input$()
print "Hello " + name$

Note that this is different from the traditional BASIC "input" command (which would allow: input name$ for example).
The function only returns text strings, but you can easily convert it to a number by combining it with the val() function.

dim number
print "Please enter a number: "
number = Val (input$ ())
print "The square root of " + number + " is " + sqrt (number)

Key state

KeyDown and ScanKeyDown

Determines whether a key is currently pressed or released.

Format:

KeyDown(character)

ScanKeyDown(scan-code)

KeyDown takes the first character of the string argument passed to it.
ScanKeyDown takes a numeric virtual key code, often a VK_x constant (such as VK_UP e.t.c. Click "Help|Functions and Constants list..." then the "Constants" tab for a list).

Both functions return true (-1) if the key is being pressed or false (0) if otherwise.

Note: KeyDown("") will always return false.

Example 1:

ResizeText (5, 1)
while true
locate 0, 0
if KeyDown ("A") then print "Down"
else print " Up "
endif
wend

Example 2:

dim a#
while true
glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity ()
glTranslatef (0, 0, -5)
glRotatef (a#, 0, 0, 1)
glBegin (GL_TRIANGLES)
glVertex2f ( 0, 1.5)
glVertex2f (-1,-1)
glVertex2f ( 1,-1)
glEnd ()
SwapBuffers ()
while SyncTimer (10)
if ScanKeyDown (VK_LEFT) then a# = a# + 3: endif
if ScanKeyDown (VK_RIGHT) then a# = a# - 3: endif
wend
wend

Buffered input

Inkey$ and InScanKey

Format:

Inkey$ ()

InScanKey ()

Basic4GL buffers characters and raw scan codes typed into the output window.

Inkey$ () returns characters typed as single character strings. If no characters are buffered, Inkey$ () will return an empty string.

InScanKey () returns scan codes as integers. If no scan codes are buffered, InScanKey () returns 0.

Example:

while true: print Inkey$ (): wend

ClearKeys

Format:

ClearKeys ()

ClearKeys () clears the keyboard buffer, throwing away any keypresses that have yet to be handled by Inkey$ () or InScanKey ().

ClearKeys () is equivalent to the following code:

While Inkey$() <> "": wend
While InScanKey() <> 0: wend

Mouse input

Mouse functions

The following functions can be used to read the mouse.

Mouse_X, Mouse_Y

These functions return the position of the mouse in relation to the OpenGL window (if in windowed mode), or the screen (fullscreen mode).

Mouse_X() returns the X (horizontal) position.
Mouse_Y() returns the Y (vertical) position.

Both functions return a real value between 0 (far left, or top) and 1 (far right, or bottom).

Example 1:

print Mouse_X () + ", " + Mouse_Y (): run

Example 2:

ResizeText (80, 50)
dim x, y, char$
while true
    if not Mouse_Button (MOUSE_LBUTTON) then
        locate x, y: print char$
    endif
    x = Mouse_X () * TextCols ()
    y = Mouse_Y () * TextRows ()
    char$ = CharAt$ (x, y)
    locate x, y: print "X"
wend

Mouse_Button

Mouse_Button (index) returns true if button index is being pressed, or false if it isn't.

The left mouse button is index 0, the right is index 1 and the middle is index 2.
Alternatively you can use the following constants:

Left button: MOUSE_LBUTTON
Right button: MOUSE_RBUTTON
Middle button: MOUSE_MBUTTON

Example:

dim i               
print "Press the mouse buttons!"
while true
    locate 0, 2
    for i = 0 to 2: printr Mouse_Button (i) + " ": next
wend

Mouse_Wheel

Mouse_Wheel() returns how many notches the mouse wheel has turned since the last time Mouse_Wheel() was called (or the program started).

For example:

dim i
print "Turn the mouse wheel!"
while true
    i = i + Mouse_Wheel ()
    locate 0, 2: print i + "    "
wend

Mouse_XD(), Mouse_YD()

These functions return how far the mouse has moved since the last time Mouse_XD() or Mouse_YD() was called (respectively).

Mouse_XD() returns the X (horizontal) distance.
Mouse_YD() returns the Y (vertical) distance.

These functions are useful for first person shooter type movement, where the mouse is used to turn the player, instead of controlling a pointer on the screen.

Note: Mouse_XD() and Mouse_YD() work internally by positioning the mouse pointer in the middle of the window and measuring how far the mouse moves from that position. This means that using Mouse_X() or Mouse_Y() will produce unexpected results, and it is recommended you stick to one method or the other.

Example:

dim x#, z#
while true
    glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
    glLoadIdentity ()
    glTranslatef (0, 0, -4)
    glRotatef (z#, 0, 0, 1)
    glRotatef (x#, 1, 0, 0)
    glBegin (GL_TRIANGLES)
        glVertex2f (0, 1)
        glVertex2f (-.5, -1)
        glVertex2f ( .5, -1)
    glEnd ()
    SwapBuffers ()
    z# = z# - Mouse_XD () * 100
    x# = x# + Mouse_YD () * 100
wend

Joystick input

Note: A big thanks to Tyler Bingham for implementing the joystick support!

Basic4GL supports input from a single joystick. If more than one joystick is attached to a PC, Basic4GL will use whatever one the operating system says is first.

Joystick functions

The following functions can be used to read the joystick.

Joy_Keys

Joy_Keys() takes a snapshot of the joystick and generates appropriate keypresses. Arrow keys are generated for stick movement, and space bar and control (Ctrl) keypresses are generated for joystick buttons 0 and 1 respectively.

The keypresses can then be detected with the keyboard input functions:

Note that Inkey$() is not affected by Joy_Keys().

This effectively provides a simple and easy way of incorperating joystick and keyboard support into a program.

Example:

dim x, y
x = TextCols () / 2
y = TextRows () / 2
while true
    Joy_Keys ()
    if not ScanKeyDown (VK_SPACE) then
        locate x, y: print " "
    endif
    if ScanKeyDown (VK_LEFT)    and x > 0 then                  x = x - 1 endif
    if ScanKeyDown (VK_RIGHT)   and x < TextCols () - 1 then    x = x + 1 endif
    if ScanKeyDown (VK_UP)      and y > 0 then                  y = y - 1 endif
    if ScanKeyDown (VK_DOWN)    and y < TextRows () - 1 then    y = y + 1 endif
    locate x, y: print "X"
    Sleep (30)
wend

Joy_X, Joy_Y

Joy_X() returns the X (horizontal) position.
Joy_Y() returns the Y (vertical) position.

Both functions return a value from -32768 (far left, or top) to 32767 (far right or bottom).
0 is the centre of each axis. (If you have a stable, properly calibrated digital joystick.)

Example:

print Joy_X () + ", " + Joy_Y (): run

Joy_Button

Joy_Button(index) returns true if button index is currently being pressed, or false if isn't.

The first joystick button is index 0. The second is index 1 e.t.c

Example:

dim i
for i = 0 to 9

if joy_button (i) then print i: else print " ": endif

next
run

Joy_Left, Joy_Right, Joy_Up, Joy_Down

Joy_Left () returns true if the joystick is more than 100 units to the left. (This is equivalent to: Joy_X () < -100)
Joy_Right () returns true if the joystick is more than 100 units to the right. (This is equivalent to: Joy_X () > 100)
Joy_Up () returns true if the joystick is more than 100 units upwards. (This is equivalent to: Joy_Y () < -100)
Joy_Down () returns true if the joystick is more than 100 units downwards. (This is equivalent to: Joy_Y () > 100)

Joy_0, ..., Joy_9

There are also explicit functions for each joystick button from 0 through to 9.

Joy_0() returns true if the first joystick button is being pressed. (This is equivalent to: Joy_Button(0)).
...
Joy_9() returns true if the 10th joystick button is being pressed. (This is equivalent to: Joy_Button(9)).

Joystick polling

To "poll" the joystick means to take a snapshot of it's current state, including the readings of the X and Y axis and whether each button is up or down at the time of the poll.

Basic4GL automatically polls the joystick whenever one of the joystick functions is called, so you don't have to tell it to explicitly.
For example:

while true: printr Joy_X() + " " + Joy_Y () + " " + Joy_0() + " " + Joy_1 (): wend

You may want to explicitly tell Basic4GL when to poll the joystick, in order to make the program run faster.
Polling takes time (at least on older analogue joysticks). It is more efficient to poll the joystick once, and then act on the X axis, Y axis and button data captured in that poll than to poll the joystick for each axis and button that you read.

UpdateJoystick

UpdateJoystick () polls the joystick and takes a snapshot of the X and Y axis and the state of all the buttons.
Any Joy_? calls will now return the data captured at the time of the UpdateJoystick() call.
For example:

while true: UpdateJoystick (): printr Joy_X() + " " + Joy_Y () + " " + Joy_0() + " " + Joy_1 (): wend

Now instead of reading the joystick 4 times each time around the loop, we are only reading it once.
This runs significantly faster than the previous example on my PC (although my PC has an older analogue joystick attached to it.. I can't comment on digital joysticks.)

As soon as you call UpdateJoystick(), Basic4GL switches to manual joystick updates, and stays that way until your program finishes executing. Therefore you must keep calling UpdateJoystick() at the appropriate times to ensure the joystick data is up to date.
If you don't, the joystick will appear frozen, for example:

UpdateJoystick ()
while true: printr Joy_X() + " " + Joy_Y () + " " + Joy_0() + " " + Joy_1 (): wend

Here we have moved the UpdateJoystick() call out of the main loop, so it is only called once at the start of the program.
Because we don't ever call it again, each joystick functions will simply return the same value each time, i.e the state of the joystick at the start of the program when UpdateJoystick() was called.

So manual polling can be faster, but you must do it right!

File I/O

A note on security

Basic4GL programs can only read and write files from a subdirectory called "Files" in the directory where the Basic4GL program was saved.

This is for security, and is intended to protect people new to programming when trying out example programs from the internet and other sources. For all we know, the person who wrote the program might think that overwriting files in the Windows system directory is a hilarious practical joke. This way the potential damage is restricted to a small subfolder, and the people can download and run Basic4GL programs with confidence.

Obviously this means that if you distribute Basic4GL programs that use File I/O, you will have to ensure that the files read/written to end up in the appropriate "Files" subdirectory so that they can be reached.

Note: This security restriction applies to the general purpose File I/O routines described below. Other functions that load data from disk are not subject to these restrictions, in particular the image and texture loading functions can load any file they want.

Opening files

OpenFileRead and OpenFileWrite

Files are opened like so:

(For writing):

dim file
...

file = OpenFileWrite ("Files/filename.ext")

(For reading):

dim file
...

file = OpenFileRead ("Files/filename.ext")

Where filename.ext is the filename and extension that is to be opened.

file is an integer variable that will store the file handle. This is a number that Basic4GL generates to identify the file that was just opened, and will be passed to other file routines to read data from or write data to the file.

If a file is opened for writing, it replaces any file that was their previously. If no file exists, one is created.

Error handling

FileError

If a file I/O routine fails, the Basic4GL program simply keeps running, without performing the particular file operation that it attempted.

You can test whether the operation succeeded by calling the FileError () function. This is updated after every file operation. If the operation succeeded, it will be set to an error message, describing what went wrong.

For example:

dim file
file = OpenFileRead ("c:\autoexec.bat")
if FileError () <> "" then print FileError (): end endif
' Carry on...

Closing the file

CloseFile

It is good practice to close the file once you've finished with it as follows:

CloseFile (file)

If you forget, or your program stops for any reason before it can close the file, Basic4GL will close it automatically, the next time you run a Basic4GL program or when you close down Basic4GL.

File reading routines

The file must have been opened with OpenFileRead for these routines to work correctly.

ReadLine

ReadLine(file) reads a line from a text file and returns it as a string. The lines are separated by carriage return and/or newline characters.

ReadText

ReadText(file, skipEOL) skips over whitespace (spaces, tabs e.t.c) until it finds some text. It then returns all the consecutive text at that point until a whitespace character has been reached, as a string.
SkipEOL is a boolean (true/false) parameter. If it is true, then ReadText will skip over any end-of-line characters it finds in the file. If false, it will stop at the end-of-line and return a blank string.

This can be used to break up a text files into words.

ReadChar

ReadChar(file) reads a single character from the file and returns it as a string.

ReadByte

ReadByte(file) reads a single binary byte from the file and returns it as an integer.

ReadWord

ReadWord(file) reads a two byte "word" from the file and returns it as an integer.

ReadInt

ReadInt(file) reads a four byte integer from the file and returns it as an integer.

ReadFloat

ReadFloat(file) reads four bytes as a four byte floating point number and returns it as a real.

ReadDouble

ReadDouble(file) reads eight bytes as an eight byte floating point number and returns it as a real.

ReadReal

ReadReal(file) is a synonym for ReadFloat(file) in the current version of Basic4GL on the Windows platform. (Basic4GL's "real" type is equivalent to a "float" in C).

File writing routines

The file must have been opened with OpenFileWrite for these routines to work correctly.

WriteLine

WriteLine (file, text) writes text to the file and automatically appends a carriage return/newline pair.
text is a string value.

WriteString

WriteString (file, text) writes text to the file. No carriage return or linefeed is appended. A zero byte string terminator is NOT appended..
text is a string value.

WriteChar

WriteChar (file, text) writes the first character of text to the file as a single character.
text is a string value.

WriteByte

WriteByte (file, intval) writes intval to the file as a single byte value.
intval
is an integer value.

WriteWord

WriteWord (file, intval) writes intval to the file as a two byte "word" value.
intval
is an integer value.

WriteInt

WriteInt (file, intval) writes intval to the file as a four byte integer value.
intval
is an integer value.

WriteFloat

WriteFloat (file, realval) writes realval to the file as a four byte floating point value.
realval
is an real value.

WriteDouble

WriteDouble (file, realval) writes realval to the file as an eight byte floating point value.
realval
is an real value.

WriteReal

WriteReal (file, realval) is a synonym for WriteFloat (file, realval)

Other file I/O routines

EndOfFile

EndOfFile (file) applies to files opened for reading, and returns true if we have reached the end of the file.

Seek

Seek (file, offset) applies to files opened for reading, and attempts to reposition the reading position to offset bytes from the beginining of the file.

Sound

OpenAL sound support is planned for Basic4GL but has yet to be completed. Until then Basic4GL has very simple sound support, based on 3 functions (LoadSound, DeleteSound and PlaySound).

Sound functions

LoadSound

Sounds are loaded as follows:

dim sound
...
sound = LoadSound (filename)

filename must refer to a .wav file (support for other formats is planned for later versions).

PlaySound

Once the sound has been loaded, it can be played as follows:

PlaySound (sound)

DeleteSound

DeleteSound (sound) deletes the sound from memory.
If you don't explicitly delete them, Basic4GL will automatically do so when your program finishes.

General purpose functions

These functions are used for general purpose operations, such as mathematics equations and string manipulation.

abs

Abs(x) returns the absolute value of x.

asc

Asc(x) takes a single string parameter x, and returns the ASCII value of the first character.
This is the opposite of the chr$ function

atn

Atn(x) returns the Arc Tangent value of x, in radians.

beep

Beep() causes the computer to beep.

chr$

Chr$(x) takes a single integer parameter x, and returns a string character whose ASCII value is x.

Example:

Printr Chr$(72)+Chr$(101)+Chr$(108)+Chr$(108)+Chr$(111)

cos

Cos(x) returns the Cosine of x, where x is measured in radians.

cosd

Cosd(x) returns the Cosine of x, where x is measured in degrees.

exp

Exp(x) returns e raised to the power of x.

Exp is the inverse of Log.

int

Int(x) casts a real valued x to an integer.
The rounding is slightly different to the implicit type cast when a real value is assigned to an integer.
Int(x) rounds x towards negative infinity, whereas implicit type casting always rounds towards 0.

Example:

dim a#, i1, i2: a# = -5.1
i1 = a#
i2 = Int(a#)
printr "i1 = " + i1
printr "i2 = " + i2

left$

Left$(s,c) returns a string containing the first c characters of s.
s
is a string value, c is an integer value.

For example, Left$("ABCDEFG", 3) returns "ABC"

lcase$

LCase$ (x) returns x converted to lowercase.

len

Len(x) returns the length of the string x in characters.

log

Log(x) returns the natural logarithm of x.

Log is the inverse of Exp.

mid$

Mid$(s,i,c) returns a string containing c consecutive characters of string s, starting from the ith character.

For example, Mid$("ABCDEFG", 4, 3) returns "DEF".

pow

Pow(x,y) returns x raised to the power of y.

right$

Right$(s,c) returns a string containing the last c characters of s.

For example, Right$("ABCDEFG", 3) returns "EFG"

rnd

Rnd() returns a random integer value, between 0 and RND_MAX.
(RND_MAX = 32767, but could be different in future ports of Basic4GL to different platforms or operating systems.)

To return a random number between 0 and x-1 (inclusive), use:

Rnd() % x

To return a random number between 1 and x (inclusive), use:

Rnd() % x + 1

sgn

Sgn(x) returns:

1, if x is greater than 0
0, if x equals 0
-1, if x is less than 0

sin

Sin(x) returns the Sine of x, where x is measured in radians.

sind

Sind(x) returns the Sine of x, where x is measured in degrees.

sqr

Sqr(x) returns the square root of x.

(Actually the square root of the absolute value of x.)

sqrt

Sqrt(x) is exactly the same as Sqr(x)

str$

Str$(x) converts an integer value x into a string representation of x.

For example, Str$(-13.4) returns "-13.4".

tan

Tan(x) returns the Tangent of x, where x is measured in radians.

tand

Tand(x) returns the Tangent of x, where x is measured in degrees.

tanh

Tanh(x) returns the Hyperbolic Tangent of x, where x is measured in radians.

tickcount

TickCount() returns the number of milliseconds that have elapsed since the computer was turned on.

ucase$

UCase$ (x) returns x converted to uppercase.

val

Val(x) converts a string x into a numeric value.
If x cannot be converted into a number, then Val(x) returns 0.

For example, Val("27.2") returns 27.2.

Val is the opposite of Str$.

Vector and Matrix routines

Basic4GL contains built in support for matrix and vector arithmetic, through a library of trigonometry functions, and also through extensions to standard mathematical operators (+, -, * e.t.c) to work with vector and matrix types.

Vector storage format

Vectors are stored as an array of reals. For example:

dim vec#(3)
vec# = vec4 (1, 2, 3, 1) ' Create a vector and assign it to vec#

To be elegible for use with the built in trigonometry functions, the array must have 2, 3 or 4 elements. (Remember that declaring an array as size 3 actually results in 4 elements, 0 through 3 inclusive).

Element 0 stores the x component, element 1 stores y component, 2 stores z and 3 stores w.

Certain trigonometry functions that operate on 4 component vectors will automatically substitue z = 0 and/or w = 1 when short version vectors are passed in.

Matrix storage format

A matrix is a 4 x 4 array of reals, and must always be "DIM"med as:

matrixname#(3)(3)

Example:

dim matrix#(3)(3)
matrix# = IdentityMatrix () ' Assign a matrix to matrix#

The first array dimension corresponds to the x coordinate of the matrix, and the second to the y.

Basic4GL vector and matrix storage format and operations are designed to mirror those of OpenGL.
As such vectors are multiplied as column vectors on the right hand side of matrices. Matrices are stored as an array of column vectors.

Creating vectors

Vectors are just arrays, so you can read from and write to them like any other array.

dim v#(3), i
for i = 0 to 3: v#(i) = i: next ' Create a (0 1 2 3) vector

dim v1#(3), v2#(3), dotProd#
dotProd# = v1#(0)*v2#(0) + v1#(1)*v2#(1) + v1#(2)*v2#(2)
' Calculate the vector dot product
' (Note: we could also have said dotProd# = v1# * v2#)

However there are a set of routines for creating vectors quickly and simply:

vec4, vec3 and vec2

vec4(x, y, z, w) returns a 4 component vector with x, y, z and w components initialised accordingly.

vec3(x, y, z) returns a 3 component vector with x, y and z components initialised accordingly.

vec2(x, y) returns a 2 component vector with x and y components initialised accordingly.

Examples:

dim lightsource#(3)
lightsource# = vec4(0, 100, 0, 1) ' Lightsource at (0 100 0)

This is exactly equivalent to:

dim lightsource#(3)
lightsource#(0) = 0
lightsource#(1) = 100
lightsource#(2) = 0
lightsource#(3) = 1

The first version is simply a more compact alternative.

Extended mathematics operators

Certain mathematics operators have been extended to accept vectors and or matrices as input, and (where appropriate) return a vector or a matrix as a result.

vec = A vector
matrix = A matrix
real = A real value

Expression Result
-vec Returns vec negated. That is vec scaled by -1
-matrix Returns matrix negated. I.e matrix scaled by -1
vec * real
or
real * vec
Returns vector scaled by real
matrix * real
or
real * matrix
Returns matrix scaled by real
matrix * vec Returns vec multiplied as a column vector on the right hand side of matrix. The result is another vector.
matrix1 * matrix2 Returns matrix2 multiplied on the right hand side of matrix1. The result is another matrix.
vec1 * vec2 Returns the dot product of vec1 and vec2, as a real value.
vec / real Returns vec scaled by 1 / real
matrix / real Returns matrix scaled by 1 / real
vec1 + vec2 Returns vec2 added to vec1 as a vector
matrix1 + matrix2 Returns matrix2 added to matrix1 as matrix
vec1 - vec2 Returns vec2 subtracted from vec1 as a vector
matrix1 - matrix2 Returns matrix2 subtracted from matrix1 as a matrix

Matrix creation functions

These are based on the OpenGL matrix functions (glTranslate-, glRotate-, e.t.c).

MatrixZero

MatrixZero () returns a matrix where every element is zero.

dim m#(3)(3)
m# = MatrixZero ()

MatrixIdentity

MatrixIdentity () returns the identity matrix.

MatrixScale

MatrixScale (scale) returns a scale matrix

MatrixTranslate

MatrixTranslate (x, y, z) returns a translation matrix.

MatrixRotateX, MatrixRotateY and MatrixRotateZ

MatrixRotateX (angle) returns a matrix that rotates anticlockwise around the positive X axis by angle degrees.

Likewise MatrixRotateY (angle) and MatrixRotateZ (angle) return matrices that rotate around their respective axes.

There is no function for creating a rotation matrix around an arbitrary axis (like glRotate- in OpenGL) because I'm not smart enough! :-) (If anyone wants to send me the maths, I'll add one...)

MatrixBasis

MatrixBasis (vecx, vecy, vecz) creates a matrix from 3 basis vectors.

MatrixCrossProduct

MatrixCrossProduct (vec) creates a cross product matrix for vec. This matrix has the property that when multiplied with a vector v, the result is vec x v. That is the cross product of vec and v.

Using Matrices with OpenGL

glLoadMatrixf, glMultMatrixf

You can copy a standard matrix into OpenGL, replacing the perspective, model-view or texture matrix (whatever was last selected by glMatrixMode ()).
You can also multiply the current OpenGL matrix with a standard matrix.
The new matrix will transform vertices passed to OpenGL (or texture coordinates for the texture matrix), just as if you had built the matrix with glRotate-, glTranslate-, glScale-,... commands.

glLoadMatrixf (matrix) will replace the current OpenGL matrix with matrix.

glMultMatrixf (matrix) will multiply the current OpenGL matrix by matrix. The resulting matrix replaces the previous OpenGL matrix.

(Note: glLoadMatrixd and glMultMatrixd also work. However as Basic4GL works with floats internally rather than doubles, there is no particular advantage in using these functions.)

Examples:
The following examples all draw a square 10 units "into the screen", rotated anticlockwise by 20 degrees.

1.

' Standard OpenGL matrix routines
glLoadIdentity ()
glTranslatef (0, 0, -10)
glRotatef (20, 0, 0, 1)
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()

2.

' Using glMultMatrixf to multiply in basic matrices
glLoadMatrixf (MatrixIdentity ())
glMultMatrixf (MatrixTranslate (0, 0, -10))
glMultMatrixf (MatrixRotateZ (20))
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()

3.

' Build a complete matrix and load into OpenGL in one go
glLoadMatrixf (MatrixTranslate (0, 0, -10) * MatrixRotateZ (20))
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()

4.

' Matrix stored in a variable
dim m#(3)(3)
m# = MatrixTranslate (0, 0, -10) * MatrixRotateZ (20)
glLoadMatrixf (m#)
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()

Alternatively we could simply transform the vertices before passing them to OpenGL

dim m#(3)(3)
m# = MatrixTranslate (0, 0, -10) * MatrixRotateZ (20)
glBegin (GL_QUADS)
glVertex3fv (m# * vec3(-1, 1, 0))
glVertex3fv (m# * vec3(-1, -1, 0))
glVertex3fv (m# * vec3(1, -1, 0))
glVertex3fv (m# * vec3(1, 1, 0))
glEnd ()

Which works just as well.
However, keep in mind that if we perform the transformations ourselves we deny OpenGL the opportunity to perform the transformations, and make use of any optimisations such as hardware transformations supported on modern 3D graphics cards.

Other trigonometry functions

CrossProduct

CrossProduct (vec1, vec2) returns the vector cross product of vec1 and vec2. The result is a vector.

Length

Length (vec) returns the length of vec.
This is equivalent to sqr(vec*vec)

Normalize

Normalize (vec) returns vec scaled to length 1.
This is equivalent to vec / Length(vec)

Determinant

Determinant (matrix) returns the matrix determinant of matrix. The result is a real value.

Transpose

Transpose (matrix) returns matrix transposed. (That is matrix mirrored about the diagonal.)

RTInvert

RTInvert (matrix) returns matrix inverted, for any matrix containing only rotations and translations.
If matrix contains any other transformations apart from rotations and translations then the result is undefined, and will not be the inverse of matrix.

Orthonormalize

Orthonormalize (matrix) returns an orthonormal matrix by performing a series of normalizations and cross products on the basis vectors of matrix.

This is useful for matrices that are nearly orthonormal. For example to ensure a matrix (that should be orthonormal) hasn't accumulated rounding errors after a large number of transformations.

Handling the w coordinate

Some of the above functions (such as CrossProduct) and operators (such as +) take two vectors and return a single result vector. Basic4GL sets the w coordinate of the resulting vector as follows:

If this is not the behaviour that you want, you will have to set the w coordinate manually.

There is no special treatment of w when multiplying a vector by a matrix, w is calculated like any other component. You will need to divide through by w manually if this is the behaviour you require.

dim vec#(3), matrix#(3)(3)
...
vec# = matrix# * vec#		' Multiply vector by matrix
vec# = vec# / vec#(3)		' Divide through by w